全字段排序
当我们要进行排序,mysql会为当前线程分配一个叫做sort_buffer的内存来进行排序。
mysql根据sql,依照索引,把要查出来的数据从数据中一行行取出满足条件的数据全部放进sort_buffer,也就是说所有要查出来的字段全部加到sort_buffer里面。从而进行排序。
其排序方式可能就在内部完成,也可能因为内存不够数据量那么大,就要使用磁盘临时文件排序。存在sort_buffer不大,同时数据量很大的情况下,选出的数据会存为多个文件,那么很显然,我们使用归并排序的思想,对每个文件先排序,排好序后,然后用一个堆(大顶树或者小顶树)作为内存中暂存,然后一个一个元素丢到排好序的文件里面。
rowid 排序
当我们要排序的单行数据很多的时候,那么在内存的总行数就会不多,因此我们可以通过设置一个参数
SET max_length_for_sort_data = 16;
来使用另外一种算法来提高效率,这个参数就是单行数据字符数不超过16才使用全字段排序算法。和前面的全字段排序不一样的是只把id+要排序的字段加到sort_buffer里面,其余的不加进去。这样sort_buffer里面放的行数就多了很多。然后排完序后,然后更急id从主键索引里面取出所有数据。
思索
这种排序体现了mysql的一种思想:如果内存够,就要多利用内存,尽量减少磁盘访问。第二个会回表,因此,默认和优先全字段排序,但是无论怎么说,mysql排序很消耗性能。
有个办法可以不用临时表,那就是联合索引把要排序的字段加到索引里面,那就默认有序了,我们查到一个就直接返回,直到遇到不满足条件的,或者limit到极限的。
或者更加优化,使用覆盖索引。
临时表
前面说了,对于innodb来说最好使用全字段排序,因为没有最后的回表操作,减少了磁盘io,但是对于存在于内存的临时表来说,回表操作不是磁盘io,那么肯定是优先使用rowid排序。
mysql> select word from words order by rand() limit 3;
比如上面的语句就创建了临时表,使用的是memory引擎,且这个表是没有索引的。对于上面的语句来说,表有2个字段,一个字段是rand()函数生成一个随机的小数,一个就是word类型的字段。然后从表中将数据全部取出来后放到这个临时表里面。然后对这个字段排序,排序规则就是根据我们生成的这个随机数,然后我们取出这个随机数,以及位置信息放到sort_buffer里面,对随机数进行排序,然后取出数据即可。
这里的位置信息相当于innodb里面的主键,因为在memory这个存储引擎里面是没有类似索引这种操作的,他是类似于数组。
再比如,innodb里面,你删除了主键,那么innodb就会生成一个长度为6字节的rowid作为隐藏的主键。
不是所有的临时表都是内存表。这个要依据tmp_table_size这个参数来确定,默认是16m,如果大于这个值,那么就用磁盘临时表(innodb),否则则是内存临时表(memory)。
新的算法
在mysql5.6之后引用了优先队列算法。比如上面的语句只要查出前面3个,如果使用归并排序,就是对所有数据进行排序,但是我们只要查出前面3个,那么我们就可以使用一个堆结构来存,从而迅速获取到前面3个值。但是如果limit过多,那么就不用这个算法,那就使用归并算法,因为这种情况下使用优先队列算法就划不来了。
排序很消耗性能,临时表更加消耗性能
我们要尽量减少创建临时表,和排序,若是没有办法,那只能如此了。